#ifndef ____LOGGER_HPP____
#define ____LOGGER_HPP____

#include <iostream>
#include <string>
#include "mutex.hpp"
#include <filesystem>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <memory>
#include <unistd.h>

std::string GeTime()
{
    time_t cur = time(nullptr);
    struct tm Time;
    localtime_r(&cur, &Time);

    char timebuffer[128] = {0};
    snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
             Time.tm_year + 1900,
             Time.tm_mon + 1,
             Time.tm_mday,
             Time.tm_hour,
             Time.tm_min,
             Time.tm_sec);

    return timebuffer;
}

enum LogLevel
{
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

std::string GetLevel(LogLevel level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case INFO:
        return "INFO";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "UNKOWN";
    }
}


class Strategy
{
public:
    virtual ~Strategy() = default;
    virtual void SysFlush(const std::string &message) = 0;
};

class ScreenStrategy : public Strategy
{
public:
    ScreenStrategy()
    {
    }
    void SysFlush(const std::string &message) override
    {
        {
            Mutex::AutoLock mutex(_mutex);
            std::cout << message << std::endl;
        }
    }
    ~ScreenStrategy()
    {
    }

private:
    Mutex::mutex _mutex;
};

class FileStrategy : public Strategy
{

public:
    FileStrategy(std::string dirname = "./Log", std::string filename = "/log.txt")
        : _dir_name(dirname), _file_name(filename)
    {
        {
            Mutex::AutoLock mutex(_mutex);
            if (std::filesystem::exists(_dir_name))
            {
                return;
            }

            int n = mkdir(_dir_name.c_str(), 0777);
            if (n < 0)
            {
                return;
            }
        }
    }

    void SysFlush(const std::string &_message)
    {
        {
            Mutex::AutoLock mutex(_mutex);
            std::string filename = _dir_name + _file_name;
            std::ofstream out(filename.c_str(), std::ios::app);
            out << _message << '\n';
            out.close();
        }
    }

    ~FileStrategy()
    {
    }

private:
    std::string _dir_name;
    std::string _file_name;
    Mutex::mutex _mutex;
};

class Logger
{
public:
    Logger()
    {
        EnableScreen();
    }

    void EnableScreen()
    {
        _pstegy = std::make_unique<ScreenStrategy>();
    }

    void EnableFile()
    {
        _pstegy = std::make_unique<FileStrategy>();
    }

    class LoggerMessage
    {
    public:
        LoggerMessage(LogLevel &level, std::string src_name, int line, Logger &logger)
            : _level(level), _src_name(src_name), _line(line), _logger(logger), _curtime(GeTime()), _pid(getpid())
        {
            std::stringstream ss;
            ss << "[" << _curtime << "] "
               << "[" << GetLevel(_level) << "] "
               << "[" << _pid << "] "
               << "[" << _src_name << "] "
               << "[" << _line << "] "
               << "- ";
            _message = ss.str();
        }

        template <class T>
        LoggerMessage &operator<<(const T &_data)
        {
            std::stringstream _sst;
            _sst << _data;
            _message += _sst.str();

            return *this;
        }

        ~LoggerMessage()
        {
            if (_logger._pstegy)
            {
                _logger._pstegy->SysFlush(_message);
            }
        }

    private:
        std::string _curtime;
        LogLevel _level;
        pid_t _pid;
        std::string _src_name;
        int _line;
        std::string _message;
        Logger &_logger;
    };

    LoggerMessage operator()(LogLevel level, std::string _name, int line)
    {
        return LoggerMessage(level, _name, line, *this);
    }

    ~Logger()
    {
    }

private:
    std::unique_ptr<Strategy> _pstegy;
};

Logger logger;

// 使用宏，简化用户操作，获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define EnableScreen() logger.EnableScreen()
#define EnableFile() logger.EnableFile()

#endif